package encsearch

import (
	"encoding/base64"
	"fhe-search-cloud/encdb"
	"fhe-search-cloud/g"
	"fhe-search-cloud/models"
	"fhe-search-cloud/utils"

	"fmt"
	"os"
	"sort"

	"gitee.com/fhe-search/fhe-search-rpc/encrpcclient"
)

type Keyword struct {
	Term string `json:"term"`
	Freq int    `json:"freq"`
}

func ExtractKeywords(content string) []Keyword {
	cutter := g.GetCutter()
	const KeyWordsNum = 20
	//cutter.Extract(content, KeyWordsNum)
	words := cutter.CutForSearch(content, true)
	freqMap := utils.RemoveDuplicatesAndCount(words)
	keywords := make([]Keyword, 0)
	// 删除非中文词，去掉字符数为1的词
	for term, freq := range freqMap {
		// 字节数要大于2倍的字符数
		// 在utf8中, 一个中文字符通常是3个字节
		chNum := utils.CharNum(term)
		if chNum > 1 && len(term) > 2*chNum {
			keywords = append(keywords, Keyword{
				Term: term,
				Freq: freq,
			})
		}
	}
	sort.Slice(keywords, func(i, j int) bool {
		return keywords[i].Freq > keywords[j].Freq
	})
	return keywords[:KeyWordsNum]
}

func UploadEncFile(title string, fileType string, content []byte, keywords []Keyword) (errThrow error) {
	db := encdb.GetEncDBConn()
	tx := db.MustBegin()
	defer func() {
		if err := recover(); err != nil {
			tx.Rollback()
			errThrow = fmt.Errorf("UploadEncFile panic: %v", err)
		}
	}()
	// 放在一个事务里
	encDoc := models.EncDocument{}
	encTitle, err := encrpcclient.SymEncUtf8(title)
	if err != nil {
		panic(err)
	}
	encFileType, err := encrpcclient.SymEncUtf8(fileType)
	if err != nil {
		panic(err)
	}
	encDoc.EncFileType = encFileType
	encDoc.EncTitle = encTitle
	// add enc doc
	encDoc.EncFilePath = "EncDataFromAdd" + "/EncDoc" + utils.GetSnowflakeID(g.MachineID)
	fileBasePath := g.GetConfig().FheSearchConfig.FileBasePath
	fileName := fileBasePath + encDoc.EncFilePath
	err = WriteEncFile(content, fileName)
	if err != nil {
		panic(err)
	}
	encDocId := encdb.AddOneEncDocument(tx, &encDoc)
	encDoc.Id = encDocId
	// add enc Term and index
	indexItems := make([]*models.IndexItem, len(keywords))
	for i := 0; i < len(keywords); i++ {
		keyword := keywords[i]
		idArr := GetTermId(keyword.Term)
		termLen := utils.CharNum(keyword.Term)
		termId := 0
		if len(idArr) != 0 {
			termId = idArr[0]
		} else {
			// add term
			fheTerm, err := encrpcclient.FheEncUtf8(keyword.Term)
			if err != nil {
				panic(err)
			}
			termItem := &models.TermItem{
				Bucket8: encrpcclient.GetBucket8(keyword.Term),
				FheTerm: fheTerm,
			}
			termId = encdb.AddOneTerm(tx, termItem, utils.CharNum(keyword.Term))
		}
		encFreq, err := encrpcclient.SymEncS32(int32(keyword.Freq))
		if err != nil {
			panic(err)
		}
		indexItems[i] = &models.IndexItem{
			TermId:  termId,
			TermLen: termLen,
			DocId:   encDocId,
			EncFreq: encFreq,
		}
	}
	// add many index item
	encdb.AddManyIndexItem(tx, indexItems)
	err = tx.Commit()
	if err != nil {
		panic(err)
	}
	return
}

// WriteEncFile 将content编码成base64字符串之后，加密写入文件
func WriteEncFile(plainContent []byte, outputFilePath string) error {
	contentBase64 := base64.StdEncoding.EncodeToString(plainContent)
	encContent, err := encrpcclient.SymEncUtf8(contentBase64)
	if err != nil {
		return err
	}
	err = WriteFile(encContent, outputFilePath)
	if err != nil {
		return err
	}
	return nil
}

func WriteFile(anyContent []byte, outputFilePath string) error {
	file, err := os.OpenFile(outputFilePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
	if err != nil {
		return err
	}
	defer file.Close()
	_, err = file.Write(anyContent)
	if err != nil {
		return err
	}
	return nil
}

func ReadEncFile(inputFilePath string) (contentBase64 string, err error) {
	encContent, err := os.ReadFile(inputFilePath)
	if err != nil {
		return "", err
	}
	contentBase64, err = encrpcclient.SymDecUtf8(encContent)
	if err != nil {
		return "", err
	}
	return contentBase64, nil
}
